/*
  This file is part of 'Trivial chess'.

  'Trivial chess' is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  'Trivial chess' is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with 'Trivial chess'.  If not, see <http://www.gnu.org/licenses/>.
*/

/* SVN FILE: $Id$ */
/**
 * @author         $Author$
 * @version        $Rev$
 * @lastrevision   $Date$
 * @filesource     $URL$
 */

#ifndef BOARDABSTRACT_H_
#define BOARDABSTRACT_H_

#include <cctype>
#include <boost/iterator/iterator_facade.hpp>
#include <boost/lexical_cast.hpp>

#include "../MeAbstract.h"
#include "../IoAbstract.h"
#include "../macros.h"

template<class Board, class S, class M, class CA, class Int>
class BoardAbstract
	: public MeAbstract<Board>
	, public IoAbstract<Board> {
public:
	typedef S Square;
	typedef M Man;
	typedef typename Man::Color Color;
	typedef CA CastlingAvailability;
public:
	void clean() {
		FOREACH(typename Square, sq)
			this->me().unset(*sq);
	}

	bool isEmpty(const Square sq) const {
		return this->me_c()[sq].isNil();
	}
public:
	std::string toStr() const {
		std::string str;

		bool first = true;
		FOREACH_R(Rank, r) {
			if ( !first ) str += "/";
			else first = false;

			int n = 0;
			FOREACH(typename File, f) {
				const Man man = this->me_c()[Square(*f,*r)];
				if ( man.isNil() ) {
					++n;
				}
				else {
					if ( n > 0 ) {
						str += boost::lexical_cast<std::string>(n);
						n = 0;
					}
					str += man.toStr();
				}
			}
			if ( n > 0 )
				str += boost::lexical_cast<std::string>(n);
		}

		str += ' ';
		str += this->me_c().sideToMove().toStr();

		str += ' ';
		str += this->me_c().castlingAvailability().toStr();

		str += ' ';
		str += this->me_c().enpassant().toStr();

		str += ' ';
		str += boost::lexical_cast<std::string>((int)this->me_c().halfmoveClock());

		str += ' ';
		str += boost::lexical_cast<std::string>((int)this->me_c().fullmoveNumber());

		return str;
	}

	void fromStr(const std::string str) {
		Int j;
		Int i = 0;
		Man man;

		typename Square::File f = Square::File::BEGIN();
		typename Square::Rank r = Square::Rank::RBEGIN();
		Square sq;

		sq.setRank(r);
		do {
			if ( isdigit(str[i]) ) {
				const int n = lexical_cast<int>(str[i]);
				for ( j = 0; j < n; ++j ) {
					sq.setFile(f);
					this->me().set(sq, Man::NIL());
					++f;
				}
			}
			else if ( '/' == str[i]  ) {
				--r;
				sq.setRank(r);
				f = Square::File::BEGIN();
			}
			else {
				man.fromStr(str.substr(i,1));
				sq.setFile(f);
				this->me().set(sq, man);
				++f;
			}
		} while ( ' ' != str[++i]  );

		++i;
		Color color;
		color.fromStr(str.substr(i,1));
		this->me().setSideToMove(color);
		++i;

		j = 0;
		do {
			++j;
		} while ( ' ' != str[i+j]  );

		CastlingAvailability ca;
		ca.fromStr(str.substr(i,j));
		this->me().setCastlingAvailability(ca);
		i += j + 1;

		Square square;
		j = 0;
		do {
			++j;
		} while ( ' ' != str[i+j]  );
		square.fromStr(str.substr(i,j));
		this->me().setEnpassant(square);
		i += j + 1;

		j = 0;
		do {
			++j;
		} while ( ' ' != str[i+j]  );

		this->me().setHalfmoveClock(lexical_cast<int>(str.substr(i,j)));

		i += j + 1;

		this->me().setFullmoveNumber(lexical_cast<int>(str.substr(i,str.length() - i)));

	}
};

#endif /* BOARDABSTRACT_H_ */
